package com.shareyi.wox.core.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
import java.util.logging.Logger;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.shareyi.wox.config.ActionBean;
import com.shareyi.wox.config.WoxConfigException;
import com.shareyi.wox.context.ActionContext;
import com.shareyi.wox.context.ServletActionContext;
import com.shareyi.wox.core.ActionConfig;
import com.shareyi.wox.core.ActionInvocation;
import com.shareyi.wox.core.ActionProxy;
import com.shareyi.wox.core.Configuration;
import com.shareyi.wox.core.ObjectFactory;
import com.shareyi.wox.core.WoxException;
import com.shareyi.wox.util.FillFieldUtil;


/**
 * The Default ActionProxy implementation
 *
 * @author Rainer Hermanns
 * @author Revised by <a href="mailto:hu_pengfei@yahoo.com.cn">Henry Hu</a>
 * @author tmjee
 * @version $Date: 2011-12-05 09:31:39 +0100 (Mon, 05 Dec 2011) $ $Id: DefaultActionProxy.java 1210360 2011-12-05 08:31:39Z lukaszlenart $
 * @since 2005-8-6
 */
public class DefaultActionProxy implements ActionProxy, Serializable {

    private static final long serialVersionUID = 3293074152487468527L;

    private static final Log LOG = LogFactory.getLog(DefaultActionProxy.class);

    protected Object action;
    protected Configuration configuration;
    protected ActionBean actionBean;
    protected ActionInvocation invocation;
    protected String actionName;
    protected String namespace;
    protected String method;
    protected boolean executeResult;
    protected boolean cleanupContext;
    protected Method methodObject;
    protected ObjectFactory objectFactory;
    private boolean methodSpecified = true;

    /**
     * This constructor is private so the builder methods (create*) should be used to create an DefaultActionProxy.
     * <p/>
     * The reason for the builder methods is so that you can use a subclass to create your own DefaultActionProxy instance
     * (like a RMIActionProxy).
     */
    protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {

        this.invocation = inv;
        this.cleanupContext = cleanupContext;
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating an DefaultActionProxy for namespace " + namespace + " and action name " + actionName);
        }

        this.actionName = StringEscapeUtils.escapeHtml(actionName);
        this.namespace = namespace;
        this.executeResult = executeResult;
        this.method = StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(methodName));
    }

    public void setObjectFactory(ObjectFactory factory) {
        this.objectFactory = factory;
    }

    public void setConfiguration(Configuration config) {
        this.configuration = config;
    }


    public Object getAction() {
        return this.action;
    }

    public String getActionName() {
        return actionName;
    }

   

    public void setExecuteResult(boolean executeResult) {
        this.executeResult = executeResult;
    }

    public boolean getExecuteResult() {
        return executeResult;
    }

    public ActionInvocation getInvocation() {
        return invocation;
    }

    public String getNamespace() {
        return namespace;
    }

    public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        String retCode = null;
        Object object=methodObject.invoke(action, null);
        if(object!=null){
        	retCode = object.toString();
        }
        return retCode;
    }


    public String getMethod() {
        return method;
    }

    private void resolveMethod() {
        // if the method is set to null, use the one from the configuration
        // if the one from the configuration is also null, use "execute"
        if (StringUtils.isEmpty(this.method)) {
            this.method = configuration.getMethodName();
            if (StringUtils.isEmpty(this.method)) {
                this.method = ActionConfig.DEFAULT_METHOD;
            }
            methodSpecified = false;
        }
    }

    protected void prepare() throws Exception {

            if (configuration == null) {
                throw new WoxConfigException("configuaration is null");
            }
            actionBean=configuration.getActionBean();
            
            resolveMethod();

            if (!configuration.isAllowedMethod(method)) {
                throw new WoxConfigException("Invalid method: " + method + " for action " + actionName);
            }
            	
            action=objectFactory.buildAction(actionName, namespace, actionBean, null);
            methodObject=getMethodByClassAndName(action.getClass(),method);
            if(methodObject==null){
            	throw new WoxException("method ["+method+"] is not found in action :"+actionName);
            }
            
            if(methodObject.isAccessible()){
            	throw new WoxException("method ["+method+"] is unaccessiable  in action :"+actionName);
            }
            
            //填充数据参数
            FillFieldUtil.fill(action, ServletActionContext.getRequest().getParameterMap());
            
    }


    /**
     * 获取执行的method对象，包括父类中的，直到Object类
     * @param classObj
     * @param methodName
     * @return
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    private Method getMethodByClassAndName(Class<? extends Object> classObj,
			String methodName) throws SecurityException, NoSuchMethodException {
			Method method=classObj.getMethod(methodName, null);
			if(method==null){
				Class superClass=classObj.getSuperclass();
				if(superClass!=Object.class){
					method=getMethodByClassAndName(superClass, methodName);
				}
			}
			return method;
	}

	public boolean isMethodSpecified() {
        return methodSpecified;
    }

	public Configuration getConfig() {
		return this.configuration;
	}
}
